Skip to content

feat(studio): i18n pipeline (next-intl) — en/zh catalogs + locale switcher [PR-A]#1401

Merged
ohdearquant merged 2 commits into
mainfrom
feat/studio-i18n-pipeline
Jun 11, 2026
Merged

feat(studio): i18n pipeline (next-intl) — en/zh catalogs + locale switcher [PR-A]#1401
ohdearquant merged 2 commits into
mainfrom
feat/studio-i18n-pipeline

Conversation

@ohdearquant

@ohdearquant ohdearquant commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

PR-A of the #1263 split per the rewrite roadmap — pipeline only, no page conversions.

  • Wires next-intl 3.26.5 for Next.js 16 App Router: i18n/request.ts (cookie-based locale resolution, NEXT_LOCALE, en fallback), NextIntlClientProvider in root layout, next.config.mjs plugin + turbopack resolveAlias for Next 16 compat.
  • Adds messages/en.json + messages/zh.json catalogs covering nav and common namespaces only (34 leaf keys each). runs.* and showDetail.* sections from the draft were pruned — those keys target page strings that are deferred to PR-B.
  • Adds LocaleSwitcher component mounted in the header (between ProjectChip and ThemeToggle); cookie-based persistence (1-year, SameSite=Lax), page reload on switch.
  • Converts nav/shell infrastructure (Shell, Breadcrumb, NavGroup, ProjectChip) to useTranslations("nav") — this proves the pipeline end-to-end (server locale resolution → NextIntlClientProvider → client useTranslations).
  • Pages untouched: runs/page.tsx, shows/[topic]/page.tsx, and all other pages keep hardcoded English strings. PR-B re-derives those conversions against today's pages after this merges.

What was pruned from the draft catalogs and why

Pruned namespace Reason
runs.* (51 keys) Targets page-level strings in runs/page.tsx — deferred to PR-B; page not converted here
showDetail.* (48 keys) Targets page-level strings in shows/[topic]/page.tsx — same, deferred to PR-B
metadata.* (2 keys) Not wired to any useTranslations call; metadata stays static for now
common.copy.*, common.time.*, common.duration.* Targets CopyButton, Timestamp, Duration components — not converted in PR-A

Kept: nav.* (45 keys) + common.skipToMain (1 key) — everything actually consumed by the components modified in this PR.

Catalog counts

  • en.json: 34 leaf keys kept (nav.* incl. localeSwitcher, common.skipToMain — the skip link is now the single translated one in layout.tsx)
  • zh.json: 34 leaf keys (structural parity with en verified, Simplified Chinese)
  • Pruned from draft: 101 keys (runs: 51, showDetail: 48, metadata: 2)

next-intl version

3.26.5 — installed from ^3.26.0 range. Compatible with next@16.2.6 (peerDep allows ^10.0.0 || ... || ^15.0.0; Next 16 satisfies this range). The turbopack.resolveAlias in next.config.mjs works around next-intl's plugin writing to experimental.turbo — produces a warning on build but does not block compilation.

Verification results

Build: npm run build — passed. All 18 routes compiled. Output:

✓ Compiled successfully in 2.0s
✓ Generating static pages using 11 workers (18/18)

TypeScript: npm run typecheck (tsc --noEmit) — clean, zero errors.

Lint: npm run lint — 0 errors, 1 pre-existing warning (react-hooks/exhaustive-deps in runs/page.tsx line 321, confirmed present on main before this PR).

Pre-commit hooks: studio-frontend-eslint and studio-frontend-prettier both passed on commit.

Python suite: No .py files were modified in this PR.

Remaining manual QA

The following cannot be verified from this environment (no browser):

  1. Click 中文 / EN button in the header — verify page reloads and nav group/item labels render in Chinese.
  2. Reload the page after switching — verify NEXT_LOCALE cookie persists and locale is preserved.
  3. Navigate between routes (Runs, Shows, Projects, etc.) — verify breadcrumb labels translate with the locale.
  4. Verify runs/page.tsx and shows/[topic]/page.tsx display unchanged English strings (no regression from PR-A).
  5. Verify mobile nav (below 768px) translates group/item labels when locale is zh.

Partially addresses #1225. Does not close #1263 (draft PR is kept open; PR-B will supersede it).

🤖 Generated with Claude Code

ohdearquant and others added 2 commits June 10, 2026 16:16
…tcher

Wire next-intl 3.26.5 infrastructure for Next.js 16 App Router: request
config (cookie-based locale, NEXT_LOCALE), NextIntlClientProvider in root
layout, en/zh message catalogs (nav + common namespace), LocaleSwitcher
component in the header, and t() conversions for Shell/Breadcrumb/NavGroup/
ProjectChip. Pages (runs, shows, etc.) keep hardcoded English strings; PR-B
re-derives page conversions after this merges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…her aria-label

Review round 1 findings:
- Two skip links rendered (layout.tsx + Shell.tsx, both hardcoded) while
  common.skipToMain sat dead in the catalogs. Consolidated to the single
  layout.tsx link (earliest tab stop) and wired it through
  getTranslations("common"); removed Shell's duplicate.
- en.json announced the locale switch in Chinese (切換到中文) to English
  screen-reader users, and in Traditional characters at that. Now
  "Switch to Chinese" — aria-labels describe the action in the current
  locale.
- Noted removal condition on the Next 16 turbopack alias workaround.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ohdearquant ohdearquant merged commit f24def6 into main Jun 11, 2026
8 checks passed
@ohdearquant ohdearquant deleted the feat/studio-i18n-pipeline branch June 11, 2026 02:05
ohdearquant added a commit that referenced this pull request Jun 11, 2026
…v keys [PR-B] (#1402)

* feat(studio): i18n page conversions — kanban/playfield/dashboard + nav keys

- Add nav catalog keys for Kanban (en: "Kanban", zh: "看板") and
  Playfield (en: "Playfield", zh: "运行场") with corresponding ITEM_KEY
  entries in NavGroup.tsx, following the pattern from PR #1401.
- Convert app/kanban/page.tsx: PageHeader title/subtitle, all six lane
  labels, and empty-state messages to kanban.* catalog keys.
- Convert app/playfield/page.tsx: PageHeader title/subtitle, table
  headers, "All projects" filter button, and empty-state to
  playfield.* catalog keys.
- Convert app/page.tsx (dashboard): PageHeader, all five MetricCard
  labels + hints, SectionHeader titles, RunsTable headers, Shows table
  headers, SystemHealthCard labels, inventory strip, and fetchError
  string to dashboard.* catalog keys.
- Both en.json and zh.json updated with all new keys; zh entries match
  the professional/concise register of existing translations.
- lib/copy.ts consumers (errors.loadRuns, empty.runs) deliberately left
  unconverted to avoid double-indirection per task constraints.
- Verified: npm run lint (0 warnings), npm run typecheck (0 errors),
  npm run build (clean, both locales embedded, no missing-key warnings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(studio): i18n completeness gaps — inventory/allTime/plurals/Unassigned

Address four mixed-language gaps identified in review:

- dashboard inventory strip: convert "playbooks", "agents", "runs total",
  "shows" to dashboard.inventory.* keys (en+zh); restructure inventory
  from flat string to nested object in both catalogs.
- dashboard rangeLabel: convert hardcoded "all time" fallback to
  dashboard.metrics.allTime key (zh: "全部时间").
- kanban pluralization: replace bare string interpolation with next-intl
  ICU plural syntax for agent count (agentCount) and run badge
  (runCount/runningCount); zh uses non-pluralizing "# 个代理" / "# 个运行"
  per Chinese grammar conventions.
- playfield Unassigned: translate display-only render sites in
  ProjectSection header and filter chip via playfield.unassigned key
  (zh: "未分配"); internal sentinel string and Map keys/sort comparisons
  remain "Unassigned" unchanged. Also convert "{runs.length} active" to
  playfield.groupActive ICU key (zh: "{count} 活跃").

Verified: npm run lint (0 warnings), npm run typecheck (0 errors),
npm run build (clean, no missing-key warnings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(studio): zh plural catalog — ICU plural wrapper for # placeholder, unify 智能体 register

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant